package com.cardone.common.web.support;

import com.cardone.common.util.StringUtils;
import com.cardone.context.DictionaryException;

/**
 * 控制访问
 * Created by Administrator on 2014/10/10.
 */
@lombok.extern.slf4j.Slf4j
public class ControlAccessSupport implements com.cardone.context.function.Execution3Function<Boolean, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, Object> {
    @lombok.Setter
    private Integer ipForAccessCount = 6000;

    private com.google.common.cache.Cache<String, Integer> ipCache;

    @lombok.Setter
    private Integer ipCacheExpireAfterAccess = 2;

    @lombok.Setter
    private Integer sessionForAccessCount = 90;

    private com.google.common.cache.Cache<String, Integer> sessionCache;

    @lombok.Setter
    private Integer sessionCacheExpireAfterAccess = 2;

    @lombok.Setter
    private Integer sessionAndUrlForAccessCount = 45;

    private com.google.common.cache.Cache<String, Integer> sessionAndUrlCache;

    @lombok.Setter
    private Integer sessionAndUrlCacheExpireAfterAccess = 2;

    @lombok.Setter
    private String redirectUrl = "/redirectAccess.html";

    @lombok.Setter
    private String[] accessUrlList = new String[]{"/access.html", "/redirectAccess.html", "/redirectLogin.html"};

    @lombok.Setter
    private Integer accessExpireAfterAccess = 600;

    private com.google.common.cache.Cache<String, Boolean> accessCache;

    public void init() {
        if (org.apache.commons.lang3.StringUtils.isBlank(redirectUrl)) {
            return;
        }

        if (org.apache.commons.lang3.ArrayUtils.isEmpty(accessUrlList)) {
            return;
        }

        if ((ipForAccessCount != null && ipForAccessCount > 0) && (ipCacheExpireAfterAccess != null && ipCacheExpireAfterAccess > 0)) {
            ipCache = com.google.common.cache.CacheBuilder.newBuilder().expireAfterAccess(ipCacheExpireAfterAccess, java.util.concurrent.TimeUnit.SECONDS).build();
        }

        if ((sessionForAccessCount != null && sessionForAccessCount > 0) && (sessionCacheExpireAfterAccess != null && sessionCacheExpireAfterAccess > 0)) {
            sessionCache = com.google.common.cache.CacheBuilder.newBuilder().expireAfterAccess(sessionCacheExpireAfterAccess, java.util.concurrent.TimeUnit.SECONDS).build();
        }

        if ((sessionAndUrlForAccessCount != null && sessionAndUrlForAccessCount > 0) && (sessionAndUrlCacheExpireAfterAccess != null && sessionAndUrlCacheExpireAfterAccess > 0)) {
            sessionAndUrlCache = com.google.common.cache.CacheBuilder.newBuilder().expireAfterAccess(sessionAndUrlCacheExpireAfterAccess, java.util.concurrent.TimeUnit.SECONDS).build();
        }

        if (accessExpireAfterAccess == null || sessionAndUrlForAccessCount < 10) {
            accessExpireAfterAccess = 600;
        }

        accessCache = com.google.common.cache.CacheBuilder.newBuilder().expireAfterAccess(accessExpireAfterAccess, java.util.concurrent.TimeUnit.SECONDS).build();
    }

    @Override
    public Boolean execution(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final Object handler) {
        if (org.apache.commons.lang3.StringUtils.isBlank(redirectUrl)) {
            return null;
        }

        final String url = com.cardone.context.ContextHolder.getServletPath(request);

        if (org.apache.commons.lang3.ArrayUtils.contains(accessUrlList, url)) {
            return true;
        }

        String ipCacheKey;

        if (ipCache != null) {
            ipCacheKey = StringUtils.defaultIfBlank(request.getHeader("x-forwarded-for"), request.getRemoteAddr(), request.getSession().getId());

            Boolean returnValue = this.execution(request, response, ipCache, ipCacheKey, ipForAccessCount, url);

            if (returnValue != null) {
                accessCache.put(ipCacheKey, returnValue);

                return returnValue;
            }
        }

        String sessionCacheKey = null;

        if (sessionCache != null) {
            sessionCacheKey = request.getSession().getId();

            Boolean returnValue = this.execution(request, response, sessionCache, sessionCacheKey, sessionForAccessCount, url);

            if (returnValue != null) {
                accessCache.put(sessionCacheKey, returnValue);

                return returnValue;
            }
        }

        String sessionAndUrlCacheKey;

        if (sessionAndUrlCache != null) {
            sessionAndUrlCacheKey = request.getSession().getId() + url;

            Boolean returnValue = this.execution(request, response, sessionAndUrlCache, sessionAndUrlCacheKey, sessionAndUrlForAccessCount, url);

            if (returnValue != null) {
                accessCache.put(sessionAndUrlCacheKey, returnValue);

                if (org.apache.commons.lang3.StringUtils.isNotBlank(sessionCacheKey)) {
                    accessCache.put(sessionCacheKey, returnValue);
                }

                return returnValue;
            }
        }

        return null;
    }

    private Boolean execution(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, final com.google.common.cache.Cache<String, Integer> cache, String key, Integer accessCount, String url) {
        try {
            Boolean isAccess = accessCache.get(key, () -> true);

            if (org.apache.commons.lang.BooleanUtils.isFalse(isAccess)) {
                toUrl(request, response, url);

                return false;
            }

            Integer keyForAccessCount = cache.get(key, () -> 0);

            keyForAccessCount++;

            cache.put(key, keyForAccessCount);

            if (keyForAccessCount > accessCount) {
                toUrl(request, response, url);

                return false;
            }
        } catch (java.util.concurrent.ExecutionException e) {
            log.error(e.getMessage(), e);
        }

        return null;
    }

    private void toUrl(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response, String url) {
        throw new DictionaryException("刷新太快了，请慢一点点").setTypeCode("url").setCode("count").setUrl(redirectUrl);
    }
}
